﻿using System;
using System.Linq;
using Henke.DevUI.Policy.Common;
using Henke.DevUI.Utils.Specifications;

namespace Henke.DevUI.Policy.MVVM.Specifications
{
    /// <summary>
    /// Useful when checking that targets does not expose certain types
    /// Returns true if the target exposes a certain type, or a subtype of that type
    /// </summary>
    /// <example>
    /// No viewmodel should expose the implementation of xxxx (expose interface instead)
    /// No viewmodel should expose observablecollections (expose the view instead, there is no reason not to)
    /// </example>
    public class Exposes : Specification<IPolicyTarget>
    {
        private readonly Type _type;

        public Exposes(Type type)
        {
            _type = type;
        }


        public override bool IsSatisfiedBy(IPolicyTarget obj)
        {
            #region generic

            if (_type.IsGenericType)
            {
                foreach (var exposedType in obj.AllExposedTypes.Where(t=>t.IsGenericType))
                {
                        if (IsEqualOrAssignable(_type.GetGenericTypeDefinition(),exposedType.GetGenericTypeDefinition()))
                        {
                            if (IsEqualOrAssignable(_type.GetGenericArguments()[0], exposedType.GetGenericArguments()[0])) return true;
                        }
                    
                }
                return false;
            }
            #endregion

            return obj.AllExposedTypes.Any(t => IsEqualOrAssignable(_type, t));
           
        }

        private static bool IsEqualOrAssignable(Type source,Type target)
        {
            return source.IsAssignableFrom(target) || source.Equals(target);
        }

    }

    /// <summary>
    /// Used for checking generics
    /// </summary>
    /// <example>
    /// If we do not want any IList of T, use AnyType />
    /// </example>
    public abstract class AnyType : Type
    {
        
    }
}
